library Practicals requires /*
    */ TimerUtils /*
    */ Math /*
    */ ZLibrary /*
    */
    
    globals
        private location tempLoc = Location(0, 0)
    endglobals

    constant function B2S takes boolean b returns string
        if b then
            return "true"
        endif
        return "false"
    endfunction

    //===============================================[System]================================================
    function RegisterTimerEvent takes real time, code func returns nothing
        local trigger tr = CreateTrigger()
        call TriggerRegisterTimerEvent(tr, time, false)
        call TriggerAddCondition(tr, Condition(func))
        set tr = null
    endfunction

    //===============================================[Widget]=================================================
    /* 物体是否存活 */
    function IsWidgetAlive takes widget wdg returns boolean
        if wdg == null then
            return false
        endif
        if GetWidgetLife(wdg) > 0.405 then
            return true
        endif
        return false
    endfunction

    /* 枚举范围内的可破坏物 */
    function EnumDestructablesInCircle takes real x, real y, real radius, code callBack returns nothing
        local rect r
        if radius >= 0 then
            set r = Rect(x - radius, y - radius, x + radius, y + radius)
            call EnumDestructablesInRect(r, Condition(callBack), null)
            call RemoveRect(r)
            set r = null
        endif
    endfunction


    //===============================================[Unit]==================================================
    /*
    *
    * 设置单位是否可移动
    *
    */
    function SetUnitMoveable takes unit u, boolean moveable returns nothing
        if moveable then
            call SetUnitPropWindow(u, GetUnitDefaultPropWindow(u))
        else
            call SetUnitPropWindow(u, 0)
        endif
    endfunction
    /*
    *
    * 设置单位是否可攻击
    *
    */
    function SetUnitAttackable takes unit u, boolean attackable returns nothing
        if attackable then
            call UnitAddAbility(u, 'Aatk')
        else
            call UnitRemoveAbility(u, 'Aatk')
        endif
    endfunction
    /*
    *
    * 创建单位，对于玩家p使用不同的模型
    *
    */
    function CreateUnitDiffModel takes player p, integer unitId1, integer unitId2, real x, real y, real facing returns unit
        local integer id = unitId1
        if GetLocalPlayer() == p then
            set id = unitId2
        endif
        return CreateUnit(p, id, x, y, facing)
    endfunction
    /*
    *
    * 单位是否存活
    *
    */
    function IsUnitAlive takes unit u returns boolean
        return IsWidgetAlive(u)
    endfunction
    /*
    *
    * 单位是否满生命
    *
    */
    function IsUnitFullLife takes unit u returns boolean
        return not (GetUnitState(u, UNIT_STATE_LIFE) < GetUnitState(u, UNIT_STATE_MAX_LIFE))
    endfunction
    /*
    *
    * 单位是否满魔法
    *
    */
    function IsUnitFullMana takes unit u returns boolean
        return not (GetUnitState(u, UNIT_STATE_MANA) < GetUnitState(u, UNIT_STATE_MAX_MANA))
    endfunction
    /*
    *
    * 单位是否可被伤害
    *
    */
    function IsUnitGost takes unit u returns boolean
        if GetUnitAbilityLevel(u,'Aloc') > 0 then //蝗虫
            return true
        endif
        if GetUnitAbilityLevel(u,'Avul') > 0 then //隐形
            return true
        endif
        return false
    endfunction
    /*
    *
    * 刷新单位属性面板
    *
    */
    function RefreshUnitUI takes unit u returns nothing
        call UnitAddAbility(u, 'Adis')
        call UnitRemoveAbility(u, 'Adis')
    endfunction
    /*
    *
    * 刷新单位所属颜色
    *
    */
    function RefreshUnitEffectColor takes nothing returns nothing
        call SetAllyColorFilterState(2)
        call SetAllyColorFilterState(0)
    endfunction
    /*
    *
    * 设置单位所属玩家是否联盟
    *
    */
    function SetUnitAlliance takes unit us, unit uy, boolean isAlliance returns nothing
        call SetPlayerAlliance(GetOwningPlayer(us), GetOwningPlayer(uy), ALLIANCE_PASSIVE, isAlliance)
        call SetPlayerAlliance(GetOwningPlayer(uy), GetOwningPlayer(us), ALLIANCE_PASSIVE, isAlliance)
    endfunction
    /*
    *
    * 为单位的联盟玩家共享单位的视野
    *
    */
    function UnitShareVisionForAlly takes unit whichUnit, boolean share returns nothing
        local integer i = 0
        loop
            exitwhen i >= bj_MAX_PLAYERS
            if IsUnitAlly(whichUnit, Player(i)) then
                call UnitShareVision(whichUnit, Player(i), share)
            endif
            set i = i + 1
        endloop
    endfunction
    /*
    *
    * 为单位的敌对玩家共享单位的视野
    *
    */
    function UnitShareVisionForEnemy takes unit whichUnit, boolean share returns nothing
        local integer i = 0
        loop
            exitwhen i >= bj_MAX_PLAYERS
            if IsUnitEnemy(whichUnit, Player(i)) then
                call UnitShareVision(whichUnit, Player(i), share)
            endif
            set i = i + 1
        endloop
    endfunction
    /*
    *
    * 为所有玩家共享单位的视野
    *
    */
    function UnitShareVisionForAll takes unit whichUnit, boolean share returns nothing
        local integer i = 0
        loop
            exitwhen i >= bj_MAX_PLAYERS
            call UnitShareVision(whichUnit, Player(i), share)
            set i = i + 1
        endloop
    endfunction

    /* 获取单位物品数量 */
    function GetUnitItemCount takes unit whichUnit returns integer
        local integer size = UnitInventorySize(whichUnit)
        local integer slot = 0
        local integer result = 0
        loop
            exitwhen slot >= size
            if UnitItemInSlot(whichUnit, slot) != null then
                set result = result + 1
            endif
            set slot = slot + 1
        endloop
    
        return result
    endfunction
    
    /* 
     * 单位物品栏是否已满
     */
    function IsUnitInventoryFull takes unit whichUnit returns boolean
        return GetUnitItemCount(whichUnit) == UnitInventorySize(whichUnit)
    endfunction
    
    /* 
    * 获取物品在单位物品栏的位置 
    */
    function GetUnitItemSlot takes unit whichUnit, item whichItem returns integer
        local integer slot = 0
        local integer size
    
        if UnitHasItem(whichUnit, whichItem) then
            set size = UnitInventorySize(whichUnit)
            loop
                if UnitItemInSlot(whichUnit, slot) == whichItem then
                    return slot
                endif
                set slot = slot + 1
                exitwhen slot >= size
            endloop
        endif
    
        return -1 // NOT_FOUND
    endfunction

    /* 
    * 物品是否存活 
    */
    function IsItemAlive takes item whichItem returns boolean
        return GetItemTypeId(whichItem) != 0 and GetWidgetLife(whichItem) > 0.405
    endfunction
    
    /* 物品是否可拾取 */
    function IsItemPickupable takes item whichItem returns boolean
        return IsItemAlive(whichItem) and not IsItemOwned(whichItem) and IsItemVisible(whichItem)
    endfunction
    
    function SetUnitXY takes unit u, real x, real y returns nothing
        call SetUnitX(u, x)
        call SetUnitY(u, y)
    endfunction

    function SetUnitXYZ takes unit u, real x, real y, real z returns nothing
        call SetUnitX(u, x)
        call SetUnitY(u, y)
        call SetUnitZ(u, z)
    endfunction

    function GetAngleByUnitXY takes unit u1, unit u2 returns real
        local real x1 = GetUnitX(u1)
        local real y1 = GetUnitY(u1)
        local real x2 = GetUnitX(u2)
        local real y2 = GetUnitY(u2)
        return GetAngleByXY(x1, y1, x2, y2)
    endfunction

    function GetDistanceByUnitXY takes unit u1, unit u2 returns real
        local real x1 = GetUnitX(u1)
        local real y1 = GetUnitY(u1)
        local real x2 = GetUnitX(u2)
        local real y2 = GetUnitY(u2)
        return GetDistanceByXY(x1, y1, x2, y2)
    endfunction

    function GetDistanceByUnitXYZ takes unit u1,unit u2 returns real
        local real x1 = GetUnitX(u1)
        local real y1 = GetUnitY(u1)
        local real z1 = GetUnitZ(u1)
        local real x2 = GetUnitX(u2)
        local real y2 = GetUnitY(u2)
        local real z2 = GetUnitZ(u2)
        return GetDistanceByXYZ(x1, y1, z1, x2, y2, z2)
    endfunction

    function GetRandomLocationInRangeXY takes real x, real y, real r returns location
        local real R = GetRandomReal(0.00, r / 2)
        local real A = GetRandomReal(0.00, 2 * bj_PI)
        local real X = x + R * Cos(A)
        local real Y = y + R * Sin(A)
        return Location(X, Y)
    endfunction

    //======================================================[Player]==============================================================
    function IsPlayerPlaying takes player p returns boolean
        return GetPlayerSlotState(p) == PLAYER_SLOT_STATE_PLAYING
    endfunction

    function IsPlayerControllerIsUser takes player p returns boolean
        return GetPlayerController(p) == MAP_CONTROL_USER
    endfunction

    function SetPlayerAllianceEx takes player playera, player playerb, boolean isAlliance returns nothing
        call SetPlayerAlliance(playera, playerb, ALLIANCE_PASSIVE, isAlliance)
        call SetPlayerAlliance(playerb, playera, ALLIANCE_PASSIVE, isAlliance)
    endfunction

    function IsBeyondBound takes real x, real y returns boolean
        return (x > WorldBounds.maxX) or (x < WorldBounds.minX) or (y > WorldBounds.maxY) or (y < WorldBounds.minY)
    endfunction

    ///*==============================================================[Effect]==========================================================*/
    function AddEffectTarget takes string modelName, widget targetWidget, string attachPointName returns nothing
        call DestroyEffect(AddSpecialEffectTarget(modelName, targetWidget, attachPointName))
    endfunction

    function AddEffect takes string modelName, real x, real y returns nothing
        call DestroyEffect(AddSpecialEffect(modelName, x, y))
    endfunction

    ///*===============================================================[Camera]===========================================================*/
    //SetCameraField() has an input of degrees, but GetCameraField() returns radians.
    //This script will keep everything in degrees for ease of usage.
    function GetCameraFieldEx takes camerafield whichField returns real
        if whichField != CAMERA_FIELD_FARZ and whichField != CAMERA_FIELD_TARGET_DISTANCE and whichField != CAMERA_FIELD_ZOFFSET then
            return GetCameraField(whichField) * bj_RADTODEG
        endif
        return GetCameraField(whichField)
    endfunction
    //This function will control whether or not transmissions can be skipped
    function EnableTransmissionSkip takes boolean flag returns nothing
        if flag then
            call DestroyTrigger(bj_cineSceneBeingSkipped) //just in case it was created in this function
            call TryInitCinematicBehaviorBJ() //recreate it
        else
            if bj_cineSceneBeingSkipped == null then //if it is null, create it
                set bj_cineSceneBeingSkipped = CreateTrigger() //creating it without events will bypass the init behavior
            else
                call DisableTrigger(bj_cineSceneBeingSkipped) //disable it if it already exists
            endif
        endif
    endfunction

endlibrary
